package com.devbobcorn.donutopia.entities;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import net.minecraft.entity.AgeableEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ILivingEntityData;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.passive.FoxEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.IPacket;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.RegistryKey;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.fml.network.FMLPlayMessages;
import net.minecraftforge.fml.network.NetworkHooks;

public class ChoxEntity extends FoxEntity{
		private static final DataParameter<Integer> CHOX_TYPE_ID = EntityDataManager.defineId(ChoxEntity.class, DataSerializers.INT);

		public ChoxEntity(FMLPlayMessages.SpawnEntity packet, World world) {
			this(ModEntityTypes.CHOX, world);
		}

		protected void defineSynchedData() {
			super.defineSynchedData();
			this.entityData.define(CHOX_TYPE_ID, 0);
		}

		public ChoxEntity(EntityType<ChoxEntity> type, World world) {
			super(type, world);
			xpReward = 0;
			setNoAi(false);
			setPersistenceRequired();
		}

		@Override
		public IPacket<?> getAddEntityPacket() {
			return NetworkHooks.getEntitySpawningPacket(this);
		}

		// Modified from FOX_TYPE in super class...
		public ChoxEntity.Type getChoxVariantType() {
			return ChoxEntity.Type.getTypeByIndex(this.entityData.get(CHOX_TYPE_ID));
		}
	
		private void setChoxVariantType(ChoxEntity.Type typeIn) {
			this.entityData.set(CHOX_TYPE_ID, typeIn.getIndex());
		}

		@Override
		public void readAdditionalSaveData(CompoundNBT compound) {
			super.readAdditionalSaveData(compound);
			this.setChoxVariantType(ChoxEntity.Type.getTypeByName(compound.getString("ChoxType")));
		}

		@Override
		public void addAdditionalSaveData(CompoundNBT compound) {
			super.addAdditionalSaveData(compound);
			compound.putString("ChoxType", this.getChoxVariantType().getName());
		}

		@Override
		public ChoxEntity getBreedOffspring(ServerWorld world, AgeableEntity en) {
			ChoxEntity choxentity = (ChoxEntity)ModEntityTypes.CHOX.create(world);
			choxentity.setChoxVariantType(this.random.nextBoolean() ? this.getChoxVariantType() : ((ChoxEntity)en).getChoxVariantType());
			return choxentity;
		}

		@Nullable
		@Override
		public ILivingEntityData finalizeSpawn(IServerWorld worldIn, DifficultyInstance difficultyIn, SpawnReason reason, @Nullable ILivingEntityData spawnDataIn, @Nullable CompoundNBT dataTag) {
			Optional<RegistryKey<Biome>> optional = worldIn.getBiomeName(this.getOnPos());
			ChoxEntity.Type tp = ChoxEntity.Type.getType(optional);
			if (spawnDataIn instanceof ChoxEntity.ChoxData) {
				tp = ((ChoxEntity.ChoxData)spawnDataIn).type;
			} else {
				spawnDataIn = new ChoxEntity.ChoxData(tp);
			}
			this.setChoxVariantType(tp);
			return super.finalizeSpawn(worldIn, difficultyIn, reason, spawnDataIn, dataTag);
		}

		public static class ChoxData extends AgeableEntity.AgeableData {
			public final ChoxEntity.Type type;
	  
			public ChoxData(ChoxEntity.Type typ) {
			   super(false);
			   this.type = typ;
			}
		}

		private static final String FROZEN = "ResourceKey[minecraft:worldgen/biome / donutopia:frozen_valley]";
		
		public static enum Type {
			DARK(0, "dark", Biomes.TAIGA, Biomes.TAIGA_HILLS, Biomes.TAIGA_MOUNTAINS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_SPRUCE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.GIANT_SPRUCE_TAIGA_HILLS),
			MILKY(1, "milky", Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.SNOWY_TAIGA_MOUNTAINS);
	  
			private static final ChoxEntity.Type[] types = Arrays.stream(values()).sorted(Comparator.comparingInt(ChoxEntity.Type::getIndex)).toArray((sth) -> {
			   return new ChoxEntity.Type[sth];
			});
			private static final Map<String, ChoxEntity.Type> TYPES_BY_NAME = Arrays.stream(values()).collect(Collectors.toMap(ChoxEntity.Type::getName, (type) -> {
			   return type;
			}));
			private final int index;
			private final String name;
			private final List<RegistryKey<Biome>> spawnBiomes;

			@SafeVarargs
			private Type(int idx, String name, RegistryKey<Biome>... biomes) {
			   this.index = idx;
			   this.name = name;
			   this.spawnBiomes = Arrays.asList(biomes);
			}
	  
			public String getName() {
			   return this.name;
			}
	  
			public int getIndex() {
			   return this.index;
			}
	  
			public static ChoxEntity.Type getTypeByName(String nameIn) {
			   return TYPES_BY_NAME.getOrDefault(nameIn, DARK);
			}
	  
			public static ChoxEntity.Type getTypeByIndex(int indexIn) {
			   if (indexIn < 0 || indexIn > types.length) {
				  indexIn = 0;
			   }
	  
			   return types[indexIn];
			}
	  
			public static ChoxEntity.Type getType(Optional<RegistryKey<Biome>> biome) {
				//System.out.println("Chox!");
				if (biome.isPresent())
					return (MILKY.spawnBiomes.contains(biome.get()) || biome.get().toString().equals(FROZEN)) ? MILKY : DARK;
				else return DARK;
			}
		 }
	}